/*
 * Copyright (C) 2009 Tony Gentilcore
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.scoreninja.adapter;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager.NameNotFoundException;

/**
 * Manages communication with the ScoreNinja service.
 * 
 * Example usage:
 * public class MyGame extends Activity {
 *   private ScoreNinjaAdapter scoreNinjaAdapter;
 *   
 *   @Override
 *   protected void onCreate(Bundle savedInstanceState) {
 *     ...
 *     scoreNinjaAdapter = new ScoreNinjaAdapter(myContext, "myAppId", "myPrivateKey");
 *   }
 *   
 *   @Override
 *   protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 *     super.onActivityResult(requestCode, resultCode, data);
 *     scoreNinjaAdapter.onActivityResult(requestCode, resultCode, data);
 *   }
 *   
 *   void onGameOver() {
 *     scoreNinjaAdapter.show(playersNewScore);
 *   }
 * }
 * 
 * @author tonygentilcore@gmail.com (Tony Gentilcore)
 */
public final class ScoreNinjaAdapter {
  // Intent and Bundle keys.
  private static final String KEY_APP_ID = "appid";
  private static final String KEY_NEW_SCORE = "newscore";
  private static final String KEY_SIGNATURE = "signature";
  private static final String KEY_COMMENTS = "comments";
  private static final String KEY_LOCATION = "location";
  private static final String KEY_NAME = "name";
  private static final String KEY_TITLE = "title";
  private static final String KEY_SUB_BOARD = "subboard";
  private static final String KEY_ORIENTATION = "orientation";
  private static final int REQUEST_CODE = 987656789;  // Can be anything that is unlikely to collide.
  
  final static String PREFS_NAME = "ScoreNinjaPrefs";
  final static String NEVER_ASK_PREF = "neverask";

  public final static String PACKAGE = "com.scoreninja";
  private final static String ACTIVITY_CLASS = PACKAGE + ".ScoreNinja";
  private final static String SERVICE_CLASS = PACKAGE + ".ScoreNinjaService";

  private final Activity parent;
  private final String appId;
  private final RequestSigner signer;
  
  private int orientation = -1;

  /**
   * Creates a new ScoreNinjaAdapter which is capable of invoking the ScoreNinja
   * activity.
   * 
   * @param context The parent Activity that is creating this adapter. It is
   *          used to start the intents that invoke the ScoreNinja.
   * @param appId The application ID of the calling application. To obtain an
   *          application ID, visit http://scoreninja.appsot.com/ .
   * @param privateKey The private key associated with the given appId. Private
   *          keys are issued when new application IDs are created.
   */
  public ScoreNinjaAdapter(Context context, String appId, String privateKey) {
    this.parent = (Activity) context;
    this.appId = appId;
    this.signer = new RequestSigner(privateKey);
  }

  /**
   * By default the orientation of the scoreboard is the same as the parent
   * activity. However, if your game does something creative with orientation
   * you may override that default orientation here.
   * 
   * @param orientation Any of the SCREEN_ORIENTATION constants in
   * android.content.pm.ActivityInfo. Commonly SCREEN_ORIENTATION_LANDSCAPE or
   * SCREEN_ORIENTATION_PORTRAIT.
   */
  public void setRequestedOrientation(int orientation) {
    this.orientation = orientation;
  }
  
  /**
   * Display the top 10 list without a new score.
   * If the com.scoreninja package is not installed, the user will be prompted
   * to install it.
   */
  public void show() {
    show(0);
  }

  /**
   * Display the top 10 list with the given new score. If the newScore is in the
   * top 10, the user will be prompted to enter their name and comments.
   * Otherwise the user's ranking will be displayed at the bottom of the top 10.
   * If the com.scoreninja package is not installed, the user will be prompted
   * to install it.
   * 
   * @param newScore The most recent score achieved by the user.
   */
  public void show(int newScore) {
    show(newScore, null, null);
  }

  /**
   * Display the top 10 list with the given new score. If the newScore is in the
   * top 10, the user will be prompted to enter their name and comments.
   * Otherwise the user's ranking will be displayed at the bottom of the top 10.
   * If the com.scoreninja package is not installed, the user will be prompted
   * to install it.
   * 
   * @param newScore The most recent score achieved by the user.
   * @param titleText If non-null, the text to display in the title bar of the
   *          window.
   * @param subBoard If non-null, an arbitrary name of a sub-board to display
   *          and apply this newScore to if it qualifies. This can be used to,
   *          for example, create a separate board for "easy", "medium", "hard".
   */
  public void show(int newScore, String titleText, String subBoard) {
    if (isInstalled(parent)) {
      Intent i = new Intent();
      i.setAction(Intent.ACTION_VIEW);
      i.addCategory(Intent.CATEGORY_DEFAULT);
      i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
              | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
      i.setClassName(PACKAGE, ACTIVITY_CLASS);
      i.putExtra(KEY_APP_ID, appId);
      if (titleText != null) i.putExtra(KEY_TITLE, titleText);
      i.putExtra(KEY_NEW_SCORE, newScore);
      if (subBoard != null) i.putExtra(KEY_SUB_BOARD, subBoard);
      if (orientation != -1) i.putExtra(KEY_ORIENTATION, orientation);
      i.putExtra(KEY_SIGNATURE, signer.getSignature(appId + newScore + (subBoard != null ? subBoard : "")));
      try {
        parent.startActivityIfNeeded(i, REQUEST_CODE);
      } catch (ActivityNotFoundException e) {
        // This should not happen, but would probably mean it is not installed,
        // fail silently.
      }
    } else if (!neverAskAgain(parent)) {
      new ScoreNinjaInstallAlert(parent).show();
    }
  }

  /**
   * Must be invoked from the parent Activity's onActivityResult() method.
   * Failure to do so will prevent any new scores from being submitted.
   * 
   * @param requestCode
   * @param resultCode
   * @param intent
   */
  public void onActivityResult(int requestCode, int resultCode, Intent intent) {
    if (resultCode == Activity.RESULT_OK &&
        requestCode == REQUEST_CODE &&
        intent != null) {
      int newScore = intent.getIntExtra(KEY_NEW_SCORE, 0);
      String signature = intent.getStringExtra(KEY_SIGNATURE);
      String subBoard = intent.getStringExtra(KEY_SUB_BOARD);
      String scoreSignature = signer.getSignature(appId + newScore + (subBoard != null ? subBoard : ""));
      if (newScore < 1 || !scoreSignature.equals(signature)) {
        return;
      }

      Intent newIntent = new Intent();
      newIntent.setClassName(PACKAGE, SERVICE_CLASS);
      newIntent.setAction(Intent.ACTION_SEND);
      newIntent.putExtra(KEY_APP_ID, appId);
      newIntent.putExtra(KEY_NEW_SCORE, newScore);
      if (subBoard != null) newIntent.putExtra(KEY_SUB_BOARD, subBoard);
      if (intent.hasExtra(KEY_NAME) && intent.hasExtra(KEY_COMMENTS)) {
        String name = intent.getStringExtra(KEY_NAME);
        String comments = intent.getStringExtra(KEY_COMMENTS);
        String location = intent.getStringExtra(KEY_LOCATION);
        newIntent.putExtra(KEY_NAME, name);
        newIntent.putExtra(KEY_COMMENTS, comments);
        newIntent.putExtra(KEY_SIGNATURE, signer.getSignature(appId + newScore + (subBoard != null ? subBoard : "")
                + name + comments + (location != null ? location : "")));
      } else {
        newIntent.putExtra(KEY_SIGNATURE, scoreSignature);
      }
      parent.startService(newIntent);
    }
  }

  /**
   * Checks if we are allowed to ask to install again
   * 
   * @return A boolean that indicates whether we should never ask again
   */
  public static boolean neverAskAgain(Context ctx) {
    SharedPreferences settings = ctx.getSharedPreferences(PREFS_NAME, 0);
    return settings.getBoolean(NEVER_ASK_PREF, false);
  }
  
  /**
   * Checks if the service is installed or not
   * 
   * @return A boolean that indicates whether the service is installed
   */
  public static boolean isInstalled(Context ctx) {
    try {
      @SuppressWarnings("unused")
      Context unusedContext = ctx.createPackageContext(
              ScoreNinjaAdapter.PACKAGE, 0);
    } catch (NameNotFoundException e) {
      return false;
    }
    return true;
  }
}
